﻿/********************************************************************************
* Copyright 2010 Zane Thorn (zane.thorn@gmail.com)                              *
*                                                                               *
* NeturalMath is free software: you can redistribute it and/or modify           *
* it under the terms of the GNU Lesser General Public License as published by   *
* the Free Software Foundation, either version 3 of the License, or             *
* (at your option) any later version.                                           *
*                                                                               *
* NeturalMath is distributed in the hope that it will be useful,                *
* but WITHOUT ANY WARRANTY; without even the implied warranty of                *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
* GNU Lesser General Public License for more details.                           *
*                                                                               *
* You should have received a copy of the GNU Lesser General Public License      *
* along with NeturalMath.  If not, see <http://www.gnu.org/licenses/>.          *
********************************************************************************/

using System;
using System.Linq;
using System.Linq.Expressions;
using NeturalMath.Expressions;
using NeturalMath.Properties;

namespace NeturalMath
{
    /// <summary>
    /// Used internally by NeturalMath to represent a variable and holds values for those variables
    /// </summary>
    public sealed class Variable:MathSymbol
    {
        #region Constructors

        internal Variable(string name, BlockSymbol parentScope,MathRuntime runtime,  Scope scope)
            : base(name, parentScope,runtime, null, scope)
        {
            

        }


        #endregion

        #region Properties

        //public ExpressionValue ExpressionValue { get; private set; }
        //public ConstantValueExpression Expression { get; private set; }

        public bool IsSet
        {
            get { return Value != null; }
        }

        #endregion

        #region Public Methods

        public void SetValue(ValueExpression value)
        {
            ValidateExpression(value);

            if (Scope.Modifier == AccessabilityModifiers.Constant && Value!=null)
                throw new MathException(ErrorCodes.ConstantModified, Resources.ModifiedConstant);

            Value = value;            
        }

        public override MathValue Invoke()
        {
            if (Value == null)
                throw new MathException(ErrorCodes.VariableHasNoValue, string.Format(Resources.VariableHasNoValue,Name));

            return Value.Execute();
        }

        #endregion

        #region Helper Methods

        private void ValidateExpression(MathExpression assingment)
        {
            Func<MathExpression, bool> validator = delegate(MathExpression e)
                                                       {
                                                           var lookup = e as LookupExpression;
                                                           if (lookup!=null)
                                                           {
                                                               var symbol = lookup.GetSymbol();
                                                               if (symbol == this)
                                                                   return true;
                                                           }

                                                           return false;
                                                       };
            var validatingVisitor = new NodeFindingVisitor(validator, Runtime);

            var results = validatingVisitor.Visit(assingment);

            if (results != null)
            {
                var invalid = results.Count() > 0;

                if (invalid)
                    throw new MathException(ErrorCodes.InvalidVariableAssignment, "Invalid variable assignment");
            }
        }

        #endregion
    }
}
